import { GraphShapeUtil } from './GraphShapeUtil';

import { isEmptyArr, isEmpty, limitNum, treeForEach } from "src/util/common";
import graphUtil from './graphUtil';
import { LifelineUtil } from './behaviors/SequenceDiagram/LifelineUtil';

class ResizeUtil {
  /**
   * box schame {
   * position:String, flow/absolute/port/edge/edgeLabel defalut= flow,
      * flow元素会遵循流式布局的方式自动排布，
      * absolute的元素会按照bounds中的x,y直接定位，
      * port类型的元素会吸附的父元素的边界上，超出父元素时不需要扩展父元素，port类型的元素expandParent应为false
      * edge类型的元素不会计算bounds，其子元素不扩展父元素，会直接跳过位置和大小计算的步骤，edge类型的元素会被前置到画布的最顶层，

      * edgeLabel的定位与一般元素不同，它必定是edgeLabel定位，expandParent=false，boxX代表了在线上的比例位置，boxY代表了在线的交叉轴上的位置，left,top作为其offset.x,offset.y ,wdith 应该为auto,textWrap=false

   * expandParent:Boolean, default = true //// 是否撑开父元素 , 一些元素的nameLabel的位置在父元素外面的，此参数应设为false,
   * portDirection枚举 left/top/right/bottom //// port的朝向:
   * portPosition: 0, //// port的位置，代表port应该向容器外偏移的比例，0代表内嵌，0.5代表在中线上, 1代表外部，
   * width:String ,100 / 100% / auto  defalut=100% //// 初始宽度，可以是绝对的值（字符串）,也可以是百分比（相对父元素），这个宽度会用于boxWidth的计算，当宽度是比例值时，resizable必须是false，因为此元素的宽度只会跟着父元素的变化而变化，本生resize触发的大小变化将会失效
   * 如果style中图形是text，则width还可以设为auto，用于外部nameLabel以及edgeLabel，设为auto时，textWrap必须为false，元素的宽度会随着text的变化动态计算，但不会小于最小值30
   * height:String， 100 / 100% //// 初始高度，和宽度一样，可以时绝对值或百分比。是百分比时不能设置resizable=1
   * paddingTop:Number,  defalut=0 //// 上下左右的边距,此边据是borderbox的的，会从width和height中扣除后再计算contentWidth
   * paddingLeft:Number,defalut=0
   *  paddingBottom:Number,defalut=0
   * paddingRight:Number,defalut=0
   *
   * boxWidth: Number //// 包括padding的宽度,非用户配置，运行时根据子元素和自己的width计算得到
   * boxHeight: Number //// 包括padding的高度
   * boxX:Number //// 包括padding的X
   * boxY:Number //// 包括padding的Y
   * minWidth:Number //// 最小Box宽度，动态计算所得，text标签的最小宽度就是文本的宽度，一般元素的最小宽度是所有expandParent为true的子元素中，boxX+boxWidth的最大值
   * minHeight:Number//// 最小Box高度，动态计算所得，text标签的最小高度就是文本的高度，一般元素的最小宽度是所有expandParent为true的子元素中，boxY+boxHeight的最大值
   *
   * textWrap: Boolean, default=false//// 文本是否允许换行,不允许换行的元素编辑时按下回车键时会直接保存，不允许换行的元素，如果text内容中有换行符，换行符会被去掉
   *
   * left: number
   * top: number
   * right:Number,  //// 绝对定位并且expandParent为false时，时可用right和bottom进行靠右或者靠下定位，此参数优先级高于boxX，boxY
   * bottom:Number, ////
   *
   */

  static updateBoxByBounds(shape:UmlShape){
    const box = shape.box;
    const bounds = shape.bounds;
    if (box.position === 'edgeLabel') {
      box.left = bounds.offset?.x;
      box.top = bounds.offset?.y;

    } else  {

      box.boxX = bounds.x - box.paddingLeft;
      box.boxY =  bounds.y - box.paddingTop;
      box.boxWidth =  bounds.width + box.paddingLeft + box.paddingRight;
      box.boxHeight = bounds.height + box.paddingTop + box.paddingBottom;
    }

  }
  static updateBoundsByBox(shape:UmlShape){
    if ( !shape.bounds) shape.bounds = { x: 0, y: 0, width: 0, height: 0 };
    shape.bounds.x =  (shape.box.boxX || 0) +  shape.box.paddingLeft;
    shape.bounds.y =  (shape.box.boxY || 0) +  shape.box.paddingTop;
    shape.bounds.width = (shape.box.boxWidth || 0) - shape.box.paddingLeft - shape.box.paddingRight;
    shape.bounds.height = (shape.box.boxHeight || 0) - shape.box.paddingTop - shape.box.paddingBottom;
    if (shape.box.position === 'edgeLabel'){
      if (!shape.bounds.offset) shape.bounds.offset = { x: 0, y: 0 };
      shape.bounds.offset.x = shape.box.left || 0;
      shape.bounds.offset.y = shape.box.top || 0;
    }
  }
  /**
   * 获取port drop到画布时的吸附后的位置,调用此方法前,boxWidht和boxHeight应该已经确定，boxX和boxY已设置为调整前的值
   * @param {*} portShape
   * @param {*} changeDirection 是否需要修改朝向
   */
  static updatePortPosition( portShape:UmlShape, changeDirection:boolean) {
    const ownerShape = portShape.owner;
    if (!ownerShape) {
      console.error('port 必须有owner');
      return;
    }

    const box = portShape.box;
    if (isEmpty(box.boxX)) {
      console.log('port 未初始化，跳过autoflow');
      return;
    }
    if (changeDirection){
      const portDirection = GraphShapeUtil.getClosest(box.boxX, box.boxY, 0, 0, ownerShape.bounds.width, ownerShape.bounds.height);
      portShape.box.portDirection = portDirection;
    }
    const  portDirection = portShape.box.portDirection;
    if (portDirection === 'top') {
      box.boxY = -box.boxHeight * box.portPosition;
      box.boxX = limitNum(box.boxX, 0, ownerShape.bounds.width - box.boxWidth);

    } else if (portDirection === 'left') {
      box.boxX = -box.boxWidth * box.portPosition;
      box.boxY = limitNum(box.boxY, 0, ownerShape.bounds.height - box.boxHeight);

    } else if (portDirection === 'bottom') {
      box.boxY = ownerShape.bounds.height - box.boxHeight * (1 - box.portPosition);
      box.boxX = limitNum(box.boxX, 0, ownerShape.bounds.width - box.boxWidth);

    } else if (portDirection === 'right') {
      box.boxX = ownerShape.bounds.width - box.boxWidth * (1 - box.portPosition);
      box.boxY = limitNum(box.boxY, 0, ownerShape.bounds.height - box.boxHeight);

    }
    this.updateBoundsByBox(portShape);
    //// return bounds;

  }
  /**
   * 初始化shape，为box设置boxX, boxY, boxWidth, boxHeight,
   * 如果已经被初始化过则不会再初始化
   * @param {UmlShape} shape
   * @param {number} x 相对其父元素的x坐标
   * @param {number} y 相对其父元素的y坐标
   */
  static initShape(shape:UmlShape, x?:number, y?:number){
    console.log('init shape');
    const box = shape.box;
    shape._text = shape.text;
    //// if (!isEmpty(box.boxX)) return;

    if (isEmpty(box.boxX)) {
      box.boxX = isEmpty(x) ? box.initX : x;
    }

    if (isEmpty(box.boxY)) {
      box.boxY = isEmpty(y) ? box.initY : y;

    }

    const parent = shape.owner;
    const parentBox = parent && parent.box;
    if (box.position === 'edgeLabel') {
      box.boxWidth = 0;
      box.boxHeight = 0;
    } else if (box.position === 'movableLabel'){
      this.calcMinWidthHeight(shape);

    } else {
      if (box.widthUnit !== '%') {
        box.boxWidth =  box.boxWidth || box.width;

      } else {
        box.boxWidth = parentBox.boxWidth * box.width / 100;
      }

      if (box.heightUnit !== '%'){
        box.boxHeight = box.boxHeight || box.height;
      } else {
        box.boxHeight = parentBox.boxHeight * box.height / 100;
      }

    }
    if (box.position === 'port'){
      this.updatePortPosition(shape, true);
    }

    this.updateBoundsByBox(shape);
    //// this.updateAutoFlow(shape);
    //// let childShapes = shape.children;
    const childShapes = shape.btnShapes ? [...shape.children, ...shape.btnShapes] : shape.children;
    //// if(shape.ot)
    if (isEmptyArr(childShapes)) return;

    let curY = 0;
    childShapes.forEach(child => {
      if (!child.box.isVisible || child.box.frontHidden) return;
      if (child.box.position == "absolute"){
        this.initShape(child);
      } else if (child.box.position == "flow") {
        this.initShape(child, 0, curY);
        curY += child.box.boxHeight;
      } else if (child.box.position == "edgeLabel") {
        this.initShape(child);
      } else if (child.box.position == "port"){
        this.initShape(child, 0, 0); //// 暂时还没有这种情况，//// todo
      } else if (child.box.position == "movableLabel"){
        this.initShape(child);
      }
    });
    if (box.position === 'absolute'){
      this.calcMinWidthHeight(shape);
      //// if (shape.getTypeName() === 'UseCase'){
      this.updateAutoFlow(shape);
      //// }
    }

  }
  /**
   * 是否为字符类型的图形，字符类型的图形会自动计算字的大小
   * @param shape
   */
  static isTextShape(shape:UmlShape){
    const style = shape.box.style;
    return style.startsWith('text;') || style.startsWith('shape=imageLabel;');

  }

  ////
  /**
   * 当父元素大小变化时重新计算其子元素的位置和大小
   * @param shape
   */
  static updateAutoFlow(shape:UmlShape){
    console.log('update auto flow', shape);
    const typeName = shape.getTypeName();
    treeForEach([shape], it => {
      it._text = it.text;
    });
    if (typeName === 'UseCase'){
      this.updateUseCaseShape(shape);
      return;
    }  else if (LifelineUtil.operations[typeName]){
      this.updateAltnative(shape);
      return;
    }
    this.calcMinWidthHeight(shape);
    const childShapes = shape.btnShapes ? [...shape.children, ...shape.btnShapes] : shape.children;

    const flowShapes = [];
    const absoluteShapes = [];
    const portShapes = [];
    const edgeShapes = [];
    const edgeLabels = [];

    childShapes.forEach(childShape => {
      if (!childShape.box.isVisible || childShape.box.frontHidden) return;
      const position =  childShape.box.position;
      if (position === 'flow'){
        flowShapes.push(childShape);
      } else if (position === 'absolute') {
        absoluteShapes.push(childShape);
        //// absoluteShape.push(childShape);
      } else if (position === 'port'){
        portShapes.push(childShape);
      } else if (position === 'edge'){
        edgeShapes.push(childShape);
      } else if (position === 'edgeLabel'){
        edgeLabels.push(childShape);
      }

      //// eg. nameLabel,keywordLabel，调整宽度
      if (childShape.box.position === 'edgeLabel'){
        childShape.box.boxWidth = 0;
        childShape.box.boxHeight = 0;

      } else {
        let needReflow = false;
        if (childShape.box.widthUnit === '%'){
          const boxWidth = shape.box.boxWidth *  childShape.box.width / 100;
          //// const changed =  childShape.box.boxWidth !== boxWidth;

          //// if (changed) {
          childShape.box.boxWidth = boxWidth;
          needReflow = true;
          //// this.updateAutoFlow(childShape);
          //// }

        }
        if (childShape.box.heightUnit === '%'){
          const boxHeight = shape.box.boxHeight *  childShape.box.height / 100;
          //// const changed =  childShape.box.boxWidth !== boxWidth;

          //// if (changed) {
          childShape.box.boxHeight = boxHeight;
          needReflow = true;

          //// }

        }
        needReflow && this.updateAutoFlow(childShape);

      }

      //// if(childShape.box.heightUnit==='%') {
      ////   childShape.box.boxHeight = shape.box.boxHeight *  childShape.box.height/100
      //// }
    });

    absoluteShapes.forEach(childShape => {
      const childBox = childShape.box;
      //// eg. block的加号Icon
      if (childBox.expandParent == false && childBox.moveType == 'notAllow'){
        if (!isEmpty(childBox.left) ){
          childBox.boxX = childBox.left;

        } else if (!isEmpty(childBox.right)) {
          childBox.boxX = shape.box.boxWidth - childBox.right;

        }

        if (!isEmpty(childBox.top) ){
          childBox.boxY = childBox.top;
        } else if (!isEmpty(childBox.bottom)){
          childBox.boxY = shape.box.boxHeight - childBox.bottom;
        }
        this.updateBoundsByBox(childShape);

      }

    });
    let y = 0;
    flowShapes.forEach(childShape => {
      const childBox = childShape.box;
      childBox.boxY = y;
      //// if (childShape.box.style.startsWith('text;')){
      ////   const height = graphUtil.getTextHeight(getText(childShape), childShape.box.style, childBox.boxWidth - childBox.paddingLeft - childBox.paddingRight);
      ////   childBox.boxHeight = childShape.box.paddingTop + childShape.box.paddingBottom + height;

      //// }

      y += childBox.boxHeight;
      this.updateBoundsByBox(childShape);

    });
    if (y > shape.box.boxHeight){
      shape.box.boxHeight = y;
    }
    //// 父元素大小变化时，port的朝向应保持不变
    portShapes.forEach(childShape => {
      this.updatePortPosition(childShape, false);

    });
    //// edgeLabel的boxX,boxY是不会变的，会变的只有left,top,而left，top是跟着bounds的offsetde的x，y去更新的，其宽度和高度都固定是0，实际宽高会自动计算，所以此处不用处理edgeLabel的位置和大小变化
    //// edgeLabels.forEach(childShape=>{
    ////   childShape.box.offset.x =
    //// })
    this.updateBoundsByBox(shape);

  }
  //// 计算图形的最小大小minWidth和minHeight，对text元素计算文字大小为最小宽高，如果最小宽高大于box的size，则会增加boxWidth和boxHeight至最小宽高
  static calcMinWidthHeight(umlShape:UmlShape){
    const children = umlShape.children;
    if (children && children.length){ //// 如果有children先计算children
      let minWidth = 0, minHeight = 0;
      children.forEach(child => {
        this.calcMinWidthHeight(child);
        if (child.box.expandParent){

          const childContentWidth = (child.box.widthUnit === '%' ? child.box.minWidth : child.box.boxWidth) + child.box.boxX  + child.box.expandPaddingRight;
          if (childContentWidth > minWidth){
            minWidth = childContentWidth;
          }
          const childContentHeight = (child.box.heightUnit === '%' ? child.box.minHeight : child.box.boxHeight) +  child.box.boxY + child.box.expandPaddingBottom;
          if (childContentHeight > minHeight){
            minHeight = childContentHeight;
          }
        }
      });
      umlShape.box.minWidth = minWidth;
      umlShape.box.minHeight = minHeight;

    } else {
      //// 没有children的leaf节点
      if (this.isTextShape(umlShape)){
        if (!umlShape.box.textWrap){ //// 不允许换行
          const textSize = graphUtil.getTextSize(umlShape._text, umlShape.box.style);
          umlShape.box.minWidth = textSize.width + umlShape.box.paddingLeft + umlShape.box.paddingRight + 4;
          umlShape.box.minHeight = textSize.height + umlShape.box.paddingTop + umlShape.box.paddingBottom + 4;
        } else {
          const textSize = graphUtil.getTextSize(umlShape._text, umlShape.box.style, umlShape.box.boxWidth - umlShape.box.paddingLeft - umlShape.box.paddingRight);
          umlShape.box.minWidth = 60 + umlShape.box.paddingLeft + umlShape.box.paddingRight + 4;
          umlShape.box.minHeight = textSize.height + umlShape.box.paddingTop + umlShape.box.paddingBottom + 4;
        }
        if (umlShape.box.style.includes('horizontal=0')){
          const { minWidth, minHeight } =   umlShape.box;
          umlShape.box.minWidth = minHeight;
          umlShape.box.minHeight = minWidth;

        }
      } else {
        let minWidth = 0, minHeight = 0;
        if (umlShape.box.widthUnit === '%'){
          umlShape.box.minWidth = 0;
          umlShape.box.minHeight = 0;
        } else {
          umlShape.box.minWidth = umlShape.box.boxWidth;
          umlShape.box.minHeight = umlShape.box.boxHeight;
        }
      }
    }
    let changed = false;
    //// movableLabel的大小一直维持为文字大小
    if (umlShape.box.position === 'movableLabel'){
      if (umlShape.box.boxWidth !== umlShape.box.minWidth){
        umlShape.box.boxWidth = umlShape.box.minWidth;
        changed = true;
      }
      if (umlShape.box.boxHeight !== umlShape.box.minHeight){
        umlShape.box.boxHeight = umlShape.box.minHeight;
        changed = true;
      }
    } else if (this.isTextShape(umlShape)){
      if (umlShape.box.style.includes('horizontal=0')){
        if (umlShape.box.boxHeight < umlShape.box.minHeight){
          umlShape.box.boxHeight = umlShape.box.minHeight;
          changed = true;
        }
        if (umlShape.box.boxWidth !== umlShape.box.minWidth ){
          umlShape.box.boxWidth = umlShape.box.minWidth;
          changed = true;
        }

      } else {
        if (umlShape.box.boxWidth < umlShape.box.minWidth){
          umlShape.box.boxWidth = umlShape.box.minWidth;
          changed = true;
        }
        if (umlShape.box.boxHeight !== umlShape.box.minHeight ){
          umlShape.box.boxHeight = umlShape.box.minHeight;
          changed = true;
        }

      }

    } else {
      if (umlShape.box.boxWidth < umlShape.box.minWidth){
        umlShape.box.boxWidth = umlShape.box.minWidth;
        changed = true;
      }
      if (umlShape.box.boxHeight < umlShape.box.minHeight){
        umlShape.box.boxHeight = umlShape.box.minHeight;
        changed = true;
      }

    }

    if (changed){
      this.updateBoundsByBox(umlShape);
    }
  }

  /**
   * 递归扩展umlShape
   * @param {*} umlShape 当前umlShape，会将此umlShape与owningElement进行对比
   * @param {*} affectedShapes 不用传，用于递归
   * return 受影响的cellId
   */
  static expandParentSize(umlShape:UmlShape, affectedShapes:string[] = []){
    const parent = umlShape.owner;

    if (!parent ) return affectedShapes;
    const parentBox = parent.box;
    if (umlShape.box.expandParent === false) return affectedShapes;
    const shapeBox = umlShape.box;

    console.log('expandParentSize', umlShape, parentBox);

    const isParentDiagram = isEmpty(parent.owner);

    let resized = false; //// 父元素是否大小变化了
    //// x cell
    const xDiff = shapeBox.boxX - shapeBox.expandPaddingLeft;
    if (xDiff < 0){

      if (!isParentDiagram){
        parentBox.boxX += xDiff;
        parentBox.boxWidth -= xDiff;

        const otherChild = parent.children.filter(c => c.id !== umlShape.id && (c.box.moveType === 'allow' || c.box.moveType === 'xonly'));
        otherChild.forEach(child => {
          if (!child.bounds) return;
          if (child.box.position !== 'absolute') return;

          child.box.boxX -= xDiff;
          affectedShapes.push(child.id);
          this.updateBoundsByBox(child);
        });
        resized = true;
      }
      shapeBox.boxX = shapeBox.expandPaddingLeft;
      this.updateBoundsByBox(umlShape);
      affectedShapes.push(umlShape.id);

    }
    const yDiff = shapeBox.boxY - shapeBox.expandPaddingTop;
    if (yDiff < 0){
      if (!isParentDiagram){

        parentBox.boxY += yDiff;
        parentBox.boxHeight -= yDiff;

        const otherChild = parent.children.filter(c => c.id !== umlShape.id && (c.box.moveType === 'allow' || c.box.moveType === 'yonly'));
        otherChild.forEach(child => {
          if (!child.bounds) return;
          if (child.box.position !== 'absolute') return;
          child.box.boxY -= yDiff;
          affectedShapes.push(child.id);
          this.updateBoundsByBox(child);
        });
        resized = true;

      }
      shapeBox.boxY = shapeBox.expandPaddingTop;
      this.updateBoundsByBox(umlShape);
      affectedShapes.push(umlShape.id);

    }
    const widthDiff = parentBox.boxWidth - (shapeBox.boxX + shapeBox.boxWidth + shapeBox.expandPaddingRight);
    if (widthDiff < 0){
      parentBox.boxWidth -= widthDiff;
      resized = true;

    }
    const heightDiff = parentBox.boxHeight - (shapeBox.boxY + shapeBox.boxHeight + shapeBox.expandPaddingBottom);
    if (heightDiff < 0){
      parentBox.boxHeight -= heightDiff;
      resized = true;

    }
    if (resized) {
      affectedShapes.push(parent.id);

      this.updateBoundsByBox(parent);

      this.updateAutoFlow(parent);
      if (parent.box.position === 'flow'){
        const mainShape = GraphShapeUtil.getAbsoluteMainShape(parent);
        this.updateAutoFlow(mainShape);
        this.expandParentSize(parent, affectedShapes);
        this.expandParentSize(mainShape, affectedShapes);

      } else {
        this.expandParentSize(parent, affectedShapes);

      }

    }
    return affectedShapes;

  }
  /**
   * useCase的形状特殊，需要特殊处理，椭圆公式 x^2/a^2+y^2/b^2=1 得
   * @param shape 椭圆的图形
   */
  static updateUseCaseShape(shape:UmlShape){
    console.log('update use case', shape);
    if (shape.children.length === 1){ //// 初始情况，只有一个nameLabel
      const nameLabelShape = shape.children[0];
      const size = graphUtil.getTextSize(nameLabelShape._text, nameLabelShape.box.style);
      //// size.height += 4;
      const a = shape.box.boxWidth / 2;
      const b = shape.box.boxHeight / 2;
      let y = size.height / 2;
      let x = Math.sqrt((1 - y ** 2 / b ** 2) * a ** 2);
      let row = 1;
      let needExpand = false;
      while (x * 2 * row < size.width){
    
        row++;
        y = size.height * row / 2;
        if (y >= b) {
          row--;
          y = size.height * row / 2;
          needExpand = true;
          break;
        }
        x = Math.sqrt((1 - y ** 2 / b ** 2) * a ** 2);
        if (x < 22) {
          needExpand = true;
          break;
        }

      }
      //// debugger;
  
      if ( needExpand){ //// nameLabel撑开父元素
        console.log(needExpand, 'needExpand-------------------');
        x = size.width / 3 / 2 + 10;
        y = size.height * 3 / 2;
        let b = y + 15;
        //// shape.box.boxWidth = x * 2 + 30; ////   nameLabelShape.box.boxWidth;
        let a =  Math.sqrt(x ** 2 / (1 - y ** 2 / b ** 2));
        //// debugger;
        shape.box.boxWidth = a * 2;
        shape.box.boxHeight = b * 2;
        nameLabelShape.box.boxHeight = y * 2;
        nameLabelShape.box.boxWidth = x * 2;
  
        nameLabelShape.box.boxX = a - x;
        nameLabelShape.box.boxY = b - y;

        this.updateBoundsByBox(shape);
        this.expandParentSize(shape);

      } else {
        nameLabelShape.box.boxHeight = y * 2;
        nameLabelShape.box.boxWidth = x * 2;
  
        nameLabelShape.box.boxX = a - x;
        nameLabelShape.box.boxY = b - y;
      }

      this.updateBoundsByBox(nameLabelShape);
    } else { //// 加了compartment的情况 todo
      this.updateUseCaseComp(shape);
      console.log('user ca---------');

    }
  }
  static updateUseCaseComp(shape:UmlShape){ //// 加了compartment的情况 todo
    console.log('user ca---------');

    //// nameLabel与compartment间距写死为10；
    const gap = 10;
    const nameLabelShape2 = shape.children[0];
    const compartmentShape = shape.children[1];

    const sizes = compartmentShape.children.map(i => {

      const s =  graphUtil.getTextSize(i._text, i.box.style);
      return s;
    });
    const maxWidth = Math.max( ...sizes.map(i => i.width));
    if (shape.box.boxWidth < maxWidth + 20){
      shape.box.boxWidth = maxWidth + 20;
    }

    let compartmentHeight = 0;
    sizes.forEach(s => {
      s.height += 4;
      compartmentHeight += s.height;
    });
    
    //// label 的大小
    const nameLabelsize = graphUtil.getTextSize(nameLabelShape2.text, nameLabelShape2.box.style);

    const a = shape.box.boxWidth / 2;
    const b = shape.box.boxHeight / 2;
    let y = (nameLabelsize.height + compartmentHeight + gap) / 2;
    let x = Math.sqrt((1 - y ** 2 / b ** 2) * a ** 2);
    let row = 1;
    let needExpand = false;
    //// debugger;

    while (x * 2 * row < nameLabelsize.width){
  
      row++;
      y = (nameLabelsize.height * row + compartmentHeight + gap) / 2;
      if (y >= b) {
        row--;
        y = (nameLabelsize.height * row + compartmentHeight + gap) / 2;
        needExpand = true;
        break;
      }
      x = Math.sqrt((1 - y ** 2 / b ** 2) * a ** 2);
      if (x < 22) {
        needExpand = true;
        break;
      }

    }

    const allHeight = y * 2;

    if (shape.box.boxHeight < allHeight + 80){
      shape.box.boxHeight = allHeight + 80;
    }

    //// const a = shape.box.boxWidth / 2;
    //// const b = shape.box.boxHeight / 2;
    //// const y = allHeight / 2;
    //// const x = Math.sqrt((1 - y ** 2 / b ** 2) * a ** 2);
    nameLabelShape2.box.boxHeight = row * nameLabelsize.height;
    nameLabelShape2.box.boxWidth = 2 * x;

    nameLabelShape2.box.boxX = a - x;
    nameLabelShape2.box.boxY = b - y;

    if ( nameLabelShape2.box.boxWidth < maxWidth){ //// nameLabel撑开父元素
      shape.box.boxWidth += maxWidth - nameLabelShape2.box.boxWidth;
      nameLabelShape2.box.boxWidth = maxWidth;
      this.updateBoundsByBox(shape);
      this.expandParentSize(shape);
    }
    compartmentShape.box.boxX = nameLabelShape2.box.boxX;
    compartmentShape.box.boxY = nameLabelShape2.box.boxY + nameLabelShape2.box.boxHeight + gap;
    compartmentShape.box.boxWidth =  nameLabelShape2.box.boxWidth;
    compartmentShape.box.boxHeight =  compartmentHeight;
    this.updateBoundsByBox(compartmentShape);
    compartmentShape.children.forEach((child, index) => {
      child.box.boxX = 0;
      child.box.boxY = index == 0 ? 0 : compartmentShape.children[index - 1].box.boxHeight + compartmentShape.children[index - 1].box.boxY;
      child.box.boxWidth = compartmentShape.box.boxWidth;
      child.box.boxHeight = sizes[index].height;

      this.updateBoundsByBox(child);

    });

    this.updateBoundsByBox(nameLabelShape2);
    this.updateBoundsByBox(shape);

  }

  static updateAltnative(shape:UmlShape){
    const compart = shape.children[0];
    const operands = shape.children[0].children;
    const lastOperand = operands[operands.length - 1];
    const toUpdate:Set<UmlShape> = new Set();
    let width = shape.box.boxWidth;
    operands.forEach(op => {
      const size = graphUtil.getTextSize(op._text, op.box.style);
      width = Math.max(width, size.width);
    });
    //// 宽度同步
    if (shape.box.boxWidth !== width){
      shape.box.boxWidth = width;
      toUpdate.add(shape);
    }
    if (compart.box.boxWidth !== width){
      compart.box.boxWidth = width;
      toUpdate.add(compart);
    }
    operands.forEach((op, index) => {
      if (op.box.boxWidth !== width){
        op.box.boxWidth = width;
        toUpdate.add(op);
      }
      if (index === 0){
        if (op.box.style.includes('top=1;')){
          op.box.style = op.box.style.replace('top=1;', 'top=0;');
          toUpdate.add(op);
        }

      } else {
        if (op.box.style.includes('top=0;')){
          op.box.style = op.box.style.replace('top=0;', 'top=1;');
          toUpdate.add(op);
        }

      }
    });

    //// 同步高度

    let operandHeight = 0;
    operands.forEach(op => {
      if (op.box.boxY !== operandHeight){
        op.box.boxY = operandHeight;
        toUpdate.add(op);
      }
      operandHeight += op.box.boxHeight;
    });
    if (compart.box.boxY !== 30){
      compart.box.boxY = 30;
      toUpdate.add(compart);
    }
    if (compart.box.boxHeight !== operandHeight){
      compart.box.boxHeight = operandHeight;
      toUpdate.add(compart);
    }

    const needShapeHeight = compart.box.boxY +  compart.box.boxHeight;

    if (shape.box.boxHeight > needShapeHeight){ //// 如果外框过大,加高compart和最后一个operand
      compart.box.boxHeight = shape.box.boxHeight - 30;
      toUpdate.add(compart);
      if (lastOperand){
        lastOperand.box.boxHeight =  compart.box.boxHeight - lastOperand.box.boxY;
        toUpdate.add(lastOperand);
      }
    } else if (shape.box.boxHeight < needShapeHeight ){ //// 如果外框过小，减小compart和最后一个operand，oprand最小值约束50
      const toReduceHeight = needShapeHeight - shape.box.boxHeight;
      let canReduceHeight = 0;
      if (lastOperand){
        canReduceHeight = lastOperand.box.boxHeight - 50;
      } else {
        canReduceHeight = toReduceHeight;
      }
      const shouldReduceHeight = Math.min(toReduceHeight, canReduceHeight);
      shape.box.boxHeight =  compart.box.boxHeight + 30 - shouldReduceHeight;
      toUpdate.add(shape);
      compart.box.boxHeight = shape.box.boxHeight - 30;
      toUpdate.add(compart);
      if (lastOperand){
        lastOperand.box.boxHeight = compart.box.boxHeight - lastOperand.box.boxY;
        toUpdate.add(lastOperand);
      }
    }

    toUpdate.forEach(shape => {
      this.updateBoundsByBox(shape);
    });

  }

  static updateNodeShapes(graphNode:GraphNode, emitFresh = true){
    let needFresh = false;
    if (graphNode.getTypeName() === 'RequirementTable') return;
    graphNode.childLeafElement?.forEach((shape:UmlShape) => {
      const mainShape = GraphShapeUtil.getAbsoluteMainShape(shape);

      this.updateAutoFlow(mainShape);
      this.expandParentSize(mainShape);
      needFresh = true;

    });
    needFresh && emitFresh && app.$bus.emit('fresh-graph-debounce');
  }

}

export default ResizeUtil;
